using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel.Mappings;
using Microsoft.Practices.RepositoryFactory.SchemaDiscovery.ObjectModel.Base;
using System.Collections;

namespace Microsoft.Practices.RepositoryFactory.CommonHelpers
{
    public class EntitySerializerHelper
    {
        private XmlDocument document;

        public EntitySerializerHelper(XmlDocument rcpstateDocument)
        {
            if (rcpstateDocument == null)
                rcpstateDocument = new XmlDocument();

            document = rcpstateDocument;
        }

        public void PersistEntityMappings(ArrayList entities)
        {
            XmlNode repositoryMetadataRoot = GetOrCreateRepositoryMetadataRoot();
            foreach (Entity entity in entities)
            {
                XmlNode existingRepositoryNode = GetExistingRepositoryNode(entity.Name, repositoryMetadataRoot);

                if (existingRepositoryNode != null)
                {
                    repositoryMetadataRoot.RemoveChild(existingRepositoryNode);
                }

                XmlNode repositoryMetadata = PersistEntityMappings(entity);
                repositoryMetadataRoot.AppendChild(repositoryMetadata);
            }
        }

        private XmlNode GetOrCreateRepositoryMetadataRoot()
        {
            XmlNode repositoryMetadataRoot = document.SelectSingleNode("//repositoryInformation");
            if (repositoryMetadataRoot == null)
            {
                repositoryMetadataRoot = document.CreateElement("repositoryInformation");
                document.AppendChild(repositoryMetadataRoot);
            }
            return repositoryMetadataRoot;
        }

        private XmlNode GetExistingRepositoryNode(string entityName, XmlNode repositoryMetadataRoot)
        {
            XmlNode node = repositoryMetadataRoot.SelectSingleNode("//repositoryMetadata[@name='" + entityName + "']");
            
            return node;
        }

        public XmlNode PersistEntityMappings(Entity entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            XmlNode repositoryNode = CreateRepositoryNode(entity);

            foreach (EntityStoredProcedureMapping operationMapping in entity.StoredProcedureMappings)
            {
                XmlNode operationNode = CreateOperationNode(operationMapping);

                repositoryNode.AppendChild(operationNode);
            }

            return repositoryNode;
        }

        private XmlNode CreateOperationNode(EntityStoredProcedureMapping operationMapping)
        {
            XmlNode operationNode = document.CreateElement("operation");

            XmlAttribute operationNameAttribute = document.CreateAttribute("operationName");
            operationNameAttribute.Value = operationMapping.OperationName;
            operationNode.Attributes.Append(operationNameAttribute);


            XmlAttribute hasInputParametersAttribute = document.CreateAttribute("hasInputParameters");
            hasInputParametersAttribute.Value = operationMapping.ProcedureHasInputParameters.ToString();
            operationNode.Attributes.Append(hasInputParametersAttribute);

            XmlAttribute procedureAttribute = document.CreateAttribute("procedure");
            procedureAttribute.Value = operationMapping.Procedure;
            operationNode.Attributes.Append(procedureAttribute);

            XmlAttribute operationTypeAttribute = document.CreateAttribute("operationType");
            operationTypeAttribute.Value = operationMapping.OperationType.ToString();
            operationNode.Attributes.Append(operationTypeAttribute);

            foreach (PropertyParameterMapping property in operationMapping.ParameterMappings)
            {
                XmlNode parameterMappingNode = CreateParameterNode(property);
                operationNode.AppendChild(parameterMappingNode);
            }

            if (operationMapping.Identity != null)
            {
                XmlNode identityMappingNode = CreateIdentityNode(operationMapping.Identity);
                operationNode.AppendChild(identityMappingNode);
            }

            return operationNode;
        }

        private XmlNode CreateIdentityNode(Identity identity)
        {
            XmlNode identityNode = document.CreateElement("identity");

            XmlAttribute nameAttribute = document.CreateAttribute("name");
            nameAttribute.Value = identity.Name;
            identityNode.Attributes.Append(nameAttribute);

            foreach (PropertyParameterMapping property in identity.ParameterMappings)
            {
                XmlNode propertyMapping = CreateParameterNode(property);
                identityNode.AppendChild(propertyMapping);
            }

            return identityNode;
        }

        private XmlNode CreateParameterNode(PropertyParameterMapping property)
        {
            XmlNode node = document.CreateElement("parameter");

            XmlAttribute propertyNameAttribute = document.CreateAttribute("propertyName");
            propertyNameAttribute.Value = property.Property;
            node.Attributes.Append(propertyNameAttribute);

            XmlAttribute parameterNameAttribute = document.CreateAttribute("parameterName");
            parameterNameAttribute.Value = property.Parameter;
            node.Attributes.Append(parameterNameAttribute);

            XmlAttribute directionAttribute = document.CreateAttribute("parameterDirection");
            directionAttribute.Value = ((int)property.ParameterInstance.Direction).ToString();
            node.Attributes.Append(directionAttribute);

            return node;
        }

        private XmlNode CreateRepositoryNode(Entity entity)
        {
            XmlNode node = document.CreateElement("repositoryMetadata");
            XmlAttribute nameAttribute = document.CreateAttribute("name");
            nameAttribute.Value = entity.Name;
            node.Attributes.Append(nameAttribute);
            return node;
        }

        public bool HasStateInformation(Entity entity)
        {
            return (GetEntityStoredData(entity) != null);
        }

        public XmlNode GetEntityStoredData(Entity entity)
        {
            return document.SelectSingleNode("//repositoryMetadata[@name='" + entity.Name + "']");
        }
    }
}
